package io.mft.web.adapter.api;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import cn.hutool.core.util.ObjectUtil;
import io.mft.api.domain.Api;
import io.mft.commons.exception.MFTException;
import io.mft.commons.function.RequestValue;
import io.mft.dev.bootstrap.adapter.MFTRuningAdapter;
import io.mft.runtime.MFTApiExecutor;
import io.mft.runtime.function.ApiFunctionRequest;
import io.mft.runtime.function.ApiFunctionResponse;
import io.mft.web.adapter.api.builder.WebExecuteApiProtocolBuilder;
import io.mft.web.adapter.api.handler.ApiNotFundHandler;
import io.mft.web.adapter.api.handler.ApiProcessErrorHandler;
import io.mft.web.adapter.api.handler.OkApiHandler;
import io.mft.web.adapter.api.response.WebApiResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class WebApiRuningAdapter extends MFTRuningAdapter<ResponseEntity<Object>> implements InitializingBean{
	@Autowired
	private RequestMappingHandlerMapping requestMappingHandlerMapping;

	Map<String, RequestMappingInfo> cache=new ConcurrentHashMap<String, RequestMappingInfo>();
	@Autowired
	private WebExecuteApiProtocolBuilder apiProtocolBuilder;
	Map<String, Api> map=new HashMap<>();
	@SuppressWarnings("deprecation")
	@Override
	public void registerApi(Api api) {
		try {
			if (map.containsKey(api.getKey())) {
				return;
			}
			map.put(api.getKey(), api);
			List<String> paths = new ArrayList<>();
			if (ObjectUtil.isNotEmpty(api.getPath())) {
				paths.add(api.getPath());
			}
			String[] patternArray = new String[paths.size()];
			paths.toArray(patternArray);
			PatternsRequestCondition patterns = new PatternsRequestCondition(patternArray);

			String[] array = api.getRequestMethod().split(",");
			RequestMethod[] requestMethods = new RequestMethod[array.length];
			for (int i = 0; i < array.length; i++) {
				requestMethods[i] = RequestMethod.valueOf(array[i]);
			}
			RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition(requestMethods);
			ParamsRequestCondition params = new ParamsRequestCondition();

			HeadersRequestCondition headers = new HeadersRequestCondition();
			ConsumesRequestCondition consumes = new ConsumesRequestCondition();
			ProducesRequestCondition produces = new ProducesRequestCondition();

			RequestMappingInfo requestMappingInfo = new RequestMappingInfo(api.getKey(), patterns, methods, params,headers, consumes, produces, null);
			OkApiHandler handler = new OkApiHandler(api,this);
			Method handleMethod = ApiHandler.class.getDeclaredMethod("handle",Map.class,HttpServletRequest.class);
			requestMappingHandlerMapping.registerMapping(requestMappingInfo, handler, handleMethod);
			cache.put(api.getKey(), requestMappingInfo);
			log.warn("注册WebApi:{}->>>path:{}->>>method:{}", api.getKey(), patternArray, api.getRequestMethod());
		} catch (Throwable e) {
			log.error("", e);
		}
	}

	
	@SuppressWarnings("deprecation")
	private <T> void registerExceptionProcessApi(T handler) {
		try {
			PatternsRequestCondition patterns = new PatternsRequestCondition(handler.getClass().getSimpleName());
			RequestMethodsRequestCondition methods = new RequestMethodsRequestCondition();
			ParamsRequestCondition params = new ParamsRequestCondition();
			HeadersRequestCondition headers = new HeadersRequestCondition();
			ConsumesRequestCondition consumes = new ConsumesRequestCondition();
			ProducesRequestCondition produces = new ProducesRequestCondition();
			RequestMappingInfo requestMappingInfo = new RequestMappingInfo(handler.getClass().getName(), patterns, methods, params,headers, consumes, produces, null);
			Method handleMethod =ApiHandler.class.getDeclaredMethod("handle",Map.class,HttpServletRequest.class);
			requestMappingHandlerMapping.registerMapping(requestMappingInfo, handler, handleMethod);
		} catch (Exception e) {
			log.error("",e);
		}
	}
	
	public interface ApiExecuteParameterHandler{
		void handle(String apiKey,RequestValue parameter);
	}
	private ApiExecuteParameterHandler apiExecuteParameterHandler;
	public void setApiExecuteParameterHandler(ApiExecuteParameterHandler apiExecuteParameterHandler) {
		this.apiExecuteParameterHandler = apiExecuteParameterHandler;
	}
	@Override
	public ResponseEntity<Object> executeApi(String funcNo, Map<String, Object> parameter) {
		long start=System.currentTimeMillis();
		try {
			RequestValue requestValue = apiProtocolBuilder.buildRequest(parameter);
			if (ObjectUtil.isNotNull(apiExecuteParameterHandler)) {
				apiExecuteParameterHandler.handle(funcNo, requestValue);
			}
			log.info("接口[{}]请求参数:{},{}",funcNo,requestValue,Thread.currentThread().getName());
			ApiFunctionRequest request = new ApiFunctionRequest(requestValue);
			ApiFunctionResponse response = MFTApiExecutor.getInstance().execute(funcNo, request);
			Api api=MFTApiExecutor.getInstance().getRuntimeConfig().getApiMap().get(funcNo);
			if (api.getOutput().isResponseSimpleData()) {
				ResponseEntity<Object> responseEntity= ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(response.list());
				long end=System.currentTimeMillis();
				log.info("接口[{}]请求响应:{},耗时:{}毫秒",funcNo,response.list(),end-start);
				return responseEntity;
			}else {
				WebApiResponse webApiResponse =apiProtocolBuilder.buildWebApiResponse(response);
				
				ResponseEntity<Object> responseEntity= ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(webApiResponse);
				long end=System.currentTimeMillis();
				log.info("接口[{}]请求响应:{},耗时:{}毫秒",funcNo,webApiResponse,end-start);
				return responseEntity;
			}
		}  catch (MFTException e) {
			log.error("", e);
			WebApiResponse webApiResponse =apiProtocolBuilder.buildWebApiResponse();
			webApiResponse.setErrorInfo(e.getMessage());
			webApiResponse.setErrorNo(e.getErrorNo());
			ResponseEntity<Object> responseEntity= ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(webApiResponse);
			long end=System.currentTimeMillis();
			log.info("接口[{}]请求响应:{},耗时:{}毫秒",funcNo,webApiResponse,end-start);
			return responseEntity;
		}catch (Exception e) {
			log.error("", e);
			WebApiResponse webApiResponse =apiProtocolBuilder.buildWebApiResponse();
			webApiResponse.setErrorInfo("系统HTTP处理异常,请稍后再试");
			webApiResponse.setErrorNo(-1);
			ResponseEntity<Object> responseEntity= ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(webApiResponse);
			long end=System.currentTimeMillis();
			log.info("接口[{}]请求响应:{},耗时:{}毫秒",funcNo,webApiResponse,end-start);
			return responseEntity;
		}
	}

	@Override
	public void unregisterApi(Api api) {
		if (ObjectUtil.isNotNull(api)) {
			RequestMappingInfo mapping=cache.get(api.getKey());
			if (ObjectUtil.isNotNull(mapping)) {
				requestMappingHandlerMapping.unregisterMapping(mapping);
				cache.remove(api.getKey());
			}
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		registerExceptionProcessApi(new ApiNotFundHandler());
		registerExceptionProcessApi(new ApiProcessErrorHandler());
	}
}
